package cn.snmp.mutual.uk.stack;

import cn.snmp.mutual.uk.util.SnmpUtilities;

import java.io.*;

public abstract class AsnObject implements SnmpConstants {
    private static final String version_id = "@(#)$Id: AsnObject.java,v 3.30 2006/03/23 14:54:09 birgit Exp $ Copyright Westhawk Ltd";
    public static int debug;
    protected byte type;
    protected int startPos;
    protected int headerLength;
    protected int contentsLength;
    protected boolean isCorrect;

    public AsnObject() {
        this.startPos = 0;
        this.headerLength = 0;
        this.contentsLength = 0;
        this.isCorrect = true;
    }

    abstract void write(final OutputStream p0, final int p1) throws IOException, EncodingException;

    @Override
    public abstract String toString();

    public static void setDebug(final int newDebug) {
        AsnObject.debug = newDebug;
    }

    AsnObject AsnReadHeader(final InputStream in) throws IOException {
        return this.AsnReadHeader(in, 0);
    }

    AsnObject AsnReadHeader(final InputStream in, final int pos) throws IOException {
        AsnObject ret = null;
        final int had = in.available();
        this.type = (byte) in.read();
        if (this.type != -1) {
            final int len = this.getLengthPacket(in);
            final int off = in.available();
            final int headLength = had - off;
            int got = 0;
            final byte[] body = new byte[len];
            if (len > 0) {
                got = in.read(body, 0, len);
            }
            if (got > -1) {
                final ByteArrayInputStream buf = new ByteArrayInputStream(body);
                ret = this.AsnMakeMe(buf, this.type, len, pos, headLength);
                if (got != len) {
                    ret.isCorrect = false;
                    if (AsnObject.debug > 10) {
                        System.out.println("\nAsnObject.AsnReadHeader(): incorrect packet. Length = " + len + ", Got = " + got);
                    }
                }
            } else if (AsnObject.debug > 10) {
                System.out.println("\nAsnObject.AsnReadHeader(): Length = " + len + ", Got = " + got + ". Not processing any further.");
            }
        }
        return ret;
    }

    AsnObject AsnMakeMe(final InputStream in, final byte t, final int len, final int pos, final int headLength) throws IOException {
        AsnObject me = this;
        switch (this.type = t) {
            case 48: {
                me = new AsnSequence(in, len, pos + headLength);
                break;
            }
            case -96:
            case -95:
            case -94:
            case -93:
            case -91:
            case -90:
            case -89:
            case -88: {
                me = new AsnPduSequence(in, len, pos + headLength);
                break;
            }
            case -92: {
                me = new AsnTrapPduv1Sequence(in, len, pos + headLength);
                break;
            }
            case 2: {
                me = new AsnInteger(in, len);
                break;
            }
            case 65:
            case 66:
            case 67:
            case 71: {
                me = new AsnUnsInteger(in, len);
                break;
            }
            case 70: {
                me = new AsnUnsInteger64(in, len);
                break;
            }
            case 6: {
                me = new AsnObjectId(in, len);
                break;
            }
            case 4:
            case 64:
            case 68:
            case 69: {
                me = new AsnOctets(in, len);
                break;
            }
            case 5: {
                me = new AsnNull(in, len);
                break;
            }
            case Byte.MIN_VALUE:
            case -127:
            case -126: {
                me = new AsnPrimitive(this.type);
                break;
            }
            default: {
                if (AsnObject.debug > 0) {
                    System.out.println("AsnObject.AsnMakeMe(): Bad Type 0x" + SnmpUtilities.toHex(this.type));
                }
                me = new AsnNull(in, len);
                me.isCorrect = false;
                break;
            }
        }
        me.type = this.type;
        me.startPos = pos;
        me.headerLength = headLength;
        me.contentsLength = len;
        if (AsnObject.debug > 10) {
            System.out.println("AsnObject.AsnMakeMe(): headerLength = " + me.headerLength + ", contentsLength = " + me.contentsLength);
            System.out.println("\ttype = 0x" + SnmpUtilities.toHex(this.type) + ", " + me.getRespTypeString() + ", " + me.getClass().getName() + ": value = " + me.toString() + ", startPos = " + me.startPos + ", correct = " + me.isCorrect);
        }
        return me;
    }

    AsnObject add(final AsnObject child) {
        return child;
    }

    AsnObject findPdu() {
        return null;
    }

    AsnObject findTrapPduv1() {
        return null;
    }

    void AsnBuildHeader(final OutputStream out, final byte t, final int length) throws IOException {
        out.write(this.type = t);
        int count = this.getLengthBytes(length);
        this.headerLength = count + 1;
        this.contentsLength = length;
        if (AsnObject.debug > 10) {
            System.out.println("AsnBuildHeader(): type = 0x" + SnmpUtilities.toHex(this.type) + ", headerLength = " + this.headerLength + ", contentsLength = " + this.contentsLength);
        }
        if (count > 1) {
            --count;
            final byte tmp = (byte) (0x80 | (byte) count);
            out.write(tmp);
        }
        while (count != 0) {
            out.write((byte) (length >> (--count << 3) & 0xFF));
        }
    }

    int size() throws EncodingException {
        return 0;
    }

    int getLengthBytes(final int length) {
        if (length < 128) {
            return 1;
        }
        int mask;
        int count;
        for (mask = -16777216, count = 4; (length & mask) == 0x0; mask >>= 8, --count) {
        }
        return count + 1;
    }

    int getLengthPacket(final InputStream in) throws IOException {
        int length = 0;
        final byte mask = 127;
        final byte len = (byte) in.read();
        if ((0x80 & len) != 0x0) {
            final int count = mask & len;
            if (count < 4) {
                final byte[] data = new byte[count];
                int n = in.read(data, 0, count);
                if (n != count) {
                    throw new IOException("AsnObject.getLengthPacket(): Not enough data");
                }
                final DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
                for (n = 0; n < count; ++n) {
                    length = (length << 8) + dis.readUnsignedByte();
                }
            }
        } else {
            length = (mask & len);
        }
        return length;
    }

    int getContentsPos() {
        return this.startPos + this.headerLength;
    }

    int getContentsLength() {
        return this.contentsLength;
    }

    public byte getRespType() {
        return this.type;
    }

    public String getRespTypeString() {
        String str = "";
        switch (this.type) {
            case 1: {
                str = "ASN_BOOLEAN";
                break;
            }
            case 2: {
                str = "ASN_INTEGER";
                break;
            }
            case 3: {
                str = "ASN_BIT_STR";
                break;
            }
            case 4: {
                str = "ASN_OCTET_STR";
                break;
            }
            case 5: {
                str = "ASN_NULL";
                break;
            }
            case 6: {
                str = "ASN_OBJECT_ID";
                break;
            }
            case 16: {
                str = "ASN_SEQUENCE";
                break;
            }
            case 17: {
                str = "ASN_SET";
                break;
            }
            case 0: {
                str = "ASN_UNIVERSAL";
                break;
            }
            case -64: {
                str = "ASN_PRIVATE";
                break;
            }
            case 32: {
                str = "ASN_CONSTRUCTOR";
                break;
            }
            case 31: {
                str = "ASN_EXTENSION_ID";
                break;
            }
            case 64: {
                str = "IPADDRESS";
                break;
            }
            case 65: {
                str = "COUNTER";
                break;
            }
            case 66: {
                str = "GAUGE";
                break;
            }
            case 67: {
                str = "TIMETICKS";
                break;
            }
            case 68: {
                str = "OPAQUE";
                break;
            }
            case 69: {
                str = "NSAP_ADDRESS";
                break;
            }
            case 70: {
                str = "COUNTER64";
                break;
            }
            case 71: {
                str = "OBSOLETED_RFC1442_UINTEGER32 (SMI v1)";
                break;
            }
            case 48: {
                str = "CONS_SEQ";
                break;
            }
            case Byte.MIN_VALUE: {
                str = "SNMP_VAR_NOSUCHOBJECT";
                break;
            }
            case -127: {
                str = "SNMP_VAR_NOSUCHINSTANCE";
                break;
            }
            case -126: {
                str = "SNMP_VAR_ENDOFMIBVIEW";
                break;
            }
            case -96: {
                str = "GET_REQ_MSG";
                break;
            }
            case -95: {
                str = "GETNEXT_REQ_MSG";
                break;
            }
            case -93: {
                str = "SET_REQ_MSG";
                break;
            }
            case -91: {
                str = "GETBULK_REQ_MSG";
                break;
            }
            case -90: {
                str = "INFORM_REQ_MSG";
                break;
            }
            case -94: {
                str = "GET_RSP_MSG";
                break;
            }
            case -88: {
                str = "GET_RPRT_MSG";
                break;
            }
            case -92: {
                str = "TRP_REQ_MSG";
                break;
            }
            case -89: {
                str = "TRPV2_REQ_MSG";
                break;
            }
            default: {
                str = "0x" + SnmpUtilities.toHex(this.type);
                break;
            }
        }
        return str;
    }

    static {
        AsnObject.debug = 0;
    }
}
